//
//  TMNetwork.m
//  Pods
//
//  Created by LIJUN on 16/8/24.
//
//

#import "TMNetwork.h"
#define BseUrl @"http://123.1.153.182:8080/rpay"
typedef NS_ENUM(NSInteger, TMHTTPMethodType) {
    EHTTPMethodTypeGet = 1,
    EHTTPMethodTypePost,
};

@interface TMNetwork ( )

@property (nonatomic, strong) TMNetworkConfig *config;
@property (nonatomic, strong) AFHTTPSessionManager *manager;
@property (nonatomic, strong) NSMutableArray *requestTaskArray;

@end

@implementation TMNetwork

#pragma mark - Init & Dealloc

- (instancetype)init
{
    self = [super init];
    if ( self ) {
        self.config = [TMNetworkConfig sharedConfig];
        
        self.manager = [AFHTTPSessionManager manager];
        _manager.securityPolicy = [TMNetworkConfig sharedConfig].securityPolicy;
        _manager.operationQueue.maxConcurrentOperationCount = [TMNetworkConfig sharedConfig].maxConcurrentOperationCount;
    }
    return self;
}

#pragma mark - Private Method

- (NSMutableArray *)allTasks
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (!_requestTaskArray) {
            _requestTaskArray = [NSMutableArray array];
        }
    });
    return _requestTaskArray;
}

- (id)tryToParseData:(id)data
{
    if ([data isKindOfClass:[NSData class]]) {
        if (!data) {
            return data;
        } else {
            NSError *error = nil;
            NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
            if (!error) {
                return dict;
            } else {
                NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
                if (str) {
                    return str;
                } else {
                    return data;
                }
            }
        }
    } else {
        return data;
    }
}

- (void)haltRequestWillStart:(NSString *)path
{
    if (_config.accessoryDelegateArray) {
        for (id<TMRequestAccessoryDelegate> delegate in _config.accessoryDelegateArray) {
            if ([delegate respondsToSelector:@selector(requestWillStart:)]) {
                [delegate requestWillStart:path];
            }
        }
    }
}

- (void)haltRequestWillComplete:(NSString *)path
{
    if (_config.accessoryDelegateArray) {
        for (id<TMRequestAccessoryDelegate> delegate in _config.accessoryDelegateArray) {
            if ([delegate respondsToSelector:@selector(requestWillStart:)]) {
                [delegate requestWillComplete:path];
            }
        }
    }
}

- (NSString *)generateUrlWithPath:(NSString *)path
                      queryParams:(NSDictionary *)queryParams
{
    NSMutableString *finalUrl = [NSMutableString stringWithString:_config.baseUrl?:@""];
    [finalUrl appendString:path?:@""];
    
    if (queryParams && queryParams.count > 0) {
        [finalUrl appendString:@"?"];
        
        NSArray *keys = [queryParams allKeys];
        for (NSUInteger i = 0; i < keys.count; i ++) {
            NSString *key = keys[i];
            
            [finalUrl appendString:key];
            [finalUrl appendString:@"="];
            NSString *value = (NSString*)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(nil, (CFStringRef)[[queryParams objectForKey:key] description], nil, (CFStringRef)@":/?#[]@!$ &'()*+,;=\"<>%{}|\\^~`", kCFStringEncodingUTF8));
            [finalUrl appendString:value];
            
            if (i < (keys.count - 1)) {
                [finalUrl appendString:@"&"];
            }
        }
    }
    
    return finalUrl;
}

- (NSURLSessionTask *)requestWithPath:(NSString *)path
                           httpMethod:(TMHTTPMethodType)httpMethod
                requestSerializerType:(TMRequestSerializerType)requestSerializerType
               responseSerializerType:(TMResponseSerializerType)responseSerializerType
                         headerParams:(NSDictionary *)headerParams
                          queryParams:(NSDictionary *)queryParams
                           formParams:(id)formParams
                            withToken:(BOOL)withToken
            constructingBodyWithBlock:(AFConstructionBlock)constructingBlock
                                model:(Class)modelClass
                             progress:(TMRequestProgressBlock)progressBlock
                              success:(TMRequestSuccessBlock)successBlock
                              failure:(TMRequestFailureBlock)failureBlock
                             netError:(TMRequestNetErrorBlock)netErrorBlock
                          tokenExpire:(TMRequestTokenExpireBlock)tokenExpireBlock
{
    NSMutableDictionary *headerMutableParams = [NSMutableDictionary dictionaryWithDictionary:headerParams];
    NSMutableDictionary *queryMutableParams = [NSMutableDictionary dictionaryWithDictionary:queryParams];
    id formMutableParams = formParams;
    
//    if (_config.urlFilterDelegate) {
//        [_config.urlFilterDelegate filterUrl:path headerParams:&headerMutableParams queryParams:&queryMutableParams formParams:&formMutableParams withToken:withToken];
//    }
    
    switch (requestSerializerType) {
        case ERequestSerializerTypeHTTP:
        {
            _manager.requestSerializer = [AFHTTPRequestSerializer serializer];
            break;
        }
        case ERequestSerializerTypeJSON:
        {
            _manager.requestSerializer = [AFJSONRequestSerializer serializer];
            [_manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Accept"];
            [_manager.requestSerializer setValue:@"application/json; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
            break;
        }
        default:
            break;
    }
    _manager.requestSerializer.timeoutInterval = 100;
    
    [headerMutableParams enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        [_manager.requestSerializer setValue:obj forHTTPHeaderField:key];
    }];
    
    AFHTTPResponseSerializer *responseSerializer = nil;
    switch (responseSerializerType) {
        case EResponseSerializerTypeHTTP:
        {
            responseSerializer = [AFHTTPResponseSerializer serializer];
            break;
        }
        case EResponseSerializerTypeJSON:
        {
            responseSerializer = [AFJSONResponseSerializer serializer];
            ((AFJSONResponseSerializer *)responseSerializer).removesKeysWithNullValues = YES;
            break;
        }
        default:
            break;
    }
    responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html", @"text/plain", nil];
    _manager.responseSerializer = responseSerializer;
    
    
    NSString *url = [NSString stringWithFormat:@"%@%@",BseUrl,path];
    NSURLSessionTask *sessionTask = nil;
    
    [self haltRequestWillStart:path];
    switch (httpMethod) {
        case EHTTPMethodTypeGet:
        {
            sessionTask = [_manager GET:url parameters:formMutableParams progress:^(NSProgress * _Nonnull downloadProgress) {
                if (progressBlock) {
                    progressBlock(downloadProgress);
                }
            } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                [self haltRequestWillComplete:path];
                [[self allTasks] removeObject:task];
                
                responseObject = [self tryToParseData:responseObject];
                if (successBlock) {
                    successBlock(responseObject);
                }
                
            } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                
                if (failureBlock) {
                    failureBlock(error);
                }
            }];
            
            break;
        }
        case EHTTPMethodTypePost:
        {
            if (constructingBlock) {
                sessionTask = [_manager POST:url parameters:formMutableParams constructingBodyWithBlock:constructingBlock progress:^(NSProgress * _Nonnull uploadProgress) {
                    if (progressBlock) {
                        progressBlock(uploadProgress);
                    }
                } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                    [self haltRequestWillComplete:path];
                    [[self allTasks] removeObject:task];
                    
                    responseObject = [self tryToParseData:responseObject];
 
                    if (successBlock) {
                        successBlock(responseObject);
                    }
                } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                    [self haltRequestWillComplete:path];
                    [[self allTasks] removeObject:task];
                    
                    if (failureBlock) {
                        failureBlock(error);
                    }
                
                    
                }];
            } else {
                sessionTask = [_manager POST:url parameters:formMutableParams progress:^(NSProgress * _Nonnull uploadProgress) {
                    if (progressBlock) {
                        progressBlock(uploadProgress);
                    }
                } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                    [self haltRequestWillComplete:path];
                    [[self allTasks] removeObject:task];
                    
                    responseObject = [self tryToParseData:responseObject];

                    if (successBlock) {
                        successBlock(responseObject);
                    }
                } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                    [self haltRequestWillComplete:path];
                    [[self allTasks] removeObject:task];
                    
                    if (failureBlock) {
                        failureBlock(error);
                    }
                }];
            }
            
            break;
        }
        default:
            break;
    }
    
    [[self allTasks] addObject:sessionTask];
    return sessionTask;
}

#pragma mark - Public Method

- (void)cancelAllRequests
{
    @synchronized (self) {
        [[self allTasks] enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            if ([obj isKindOfClass:[NSURLSessionTask class]]) {
                [(NSURLSessionTask *)obj cancel];
            }
        }];
        
        [[self allTasks] removeAllObjects];
    }
}

- (void)cancelSessionTask:(NSURLSessionTask *)sessionTask
{
    @synchronized (self) {
        [sessionTask cancel];
        
        [[self allTasks] removeObject:sessionTask];
    }
}

- (NSURLSessionTask *)GET:(NSString *)path
             headerParams:(NSDictionary *)headerParams
              queryParams:(NSDictionary *)queryParams
               formParams:(id)formParams
                withToken:(BOOL)withToken
                    model:(Class)modelClass
                 progress:(TMRequestProgressBlock)progressBlock
                  success:(TMRequestSuccessBlock)successBlock
                  failure:(TMRequestFailureBlock)failureBlock
{
    return [self GET:path
        headerParams:headerParams
         queryParams:queryParams
          formParams:formParams
           withToken:withToken
               model:modelClass
            progress:progressBlock
             success:successBlock
             failure:failureBlock
            netError:nil
         tokenExpire:nil];
}

- (NSURLSessionTask *)GET:(NSString *)path
             headerParams:(NSDictionary *)headerParams
              queryParams:(NSDictionary *)queryParams
               formParams:(id)formParams
                withToken:(BOOL)withToken
                    model:(Class)modelClass
                 progress:(TMRequestProgressBlock)progressBlock
                  success:(TMRequestSuccessBlock)successBlock
                  failure:(TMRequestFailureBlock)failureBlock
                 netError:(TMRequestNetErrorBlock)netErrorBlock
              tokenExpire:(TMRequestTokenExpireBlock)tokenExpireBlock
{
    return [self GET:path
requestSerializerType:ERequestSerializerTypeHTTP
responseSerializerType:EResponseSerializerTypeJSON
        headerParams:headerParams
         queryParams:queryParams
          formParams:formParams
           withToken:withToken
               model:modelClass
            progress:progressBlock
             success:successBlock
             failure:failureBlock
            netError:netErrorBlock
         tokenExpire:tokenExpireBlock];
}

- (NSURLSessionTask *)GET:(NSString *)path
    requestSerializerType:(TMRequestSerializerType)requestSerializerType
   responseSerializerType:(TMResponseSerializerType)responseSerializerType
             headerParams:(NSDictionary *)headerParams
              queryParams:(NSDictionary *)queryParams
               formParams:(id)formParams
                withToken:(BOOL)withToken
                    model:(Class)modelClass
                 progress:(TMRequestProgressBlock)progressBlock
                  success:(TMRequestSuccessBlock)successBlock
                  failure:(TMRequestFailureBlock)failureBlock
                 netError:(TMRequestNetErrorBlock)netErrorBlock
              tokenExpire:(TMRequestTokenExpireBlock)tokenExpireBlock
{
    return [self requestWithPath:path
                      httpMethod:EHTTPMethodTypeGet
           requestSerializerType:requestSerializerType
          responseSerializerType:responseSerializerType
                    headerParams:headerParams
                     queryParams:queryParams
                      formParams:formParams
                       withToken:withToken
       constructingBodyWithBlock:nil
                           model:modelClass
                        progress:progressBlock
                         success:successBlock
                         failure:failureBlock
                        netError:netErrorBlock
                     tokenExpire:tokenExpireBlock];
}

- (NSURLSessionTask *)POST:(NSString *)path
              headerParams:(NSDictionary *)headerParams
               queryParams:(NSDictionary *)queryParams
                formParams:(id)formParams
                 withToken:(BOOL)withToken
                     model:(Class)modelClass
                  progress:(TMRequestProgressBlock)progressBlock
                   success:(TMRequestSuccessBlock)successBlock
                   failure:(TMRequestFailureBlock)failureBlock
{
    return [self POST:path
         headerParams:headerParams
          queryParams:queryParams
           formParams:formParams
            withToken:withToken
                model:modelClass
             progress:progressBlock
              success:successBlock
              failure:failureBlock
             netError:nil
          tokenExpire:nil];
}

- (NSURLSessionTask *)POST:(NSString *)path
              headerParams:(NSDictionary *)headerParams
               queryParams:(NSDictionary *)queryParams
                formParams:(id)formParams
                 withToken:(BOOL)withToken
                     model:(Class)modelClass
                  progress:(TMRequestProgressBlock)progressBlock
                   success:(TMRequestSuccessBlock)successBlock
                   failure:(TMRequestFailureBlock)failureBlock
                  netError:(TMRequestNetErrorBlock)netErrorBlock
               tokenExpire:(TMRequestTokenExpireBlock)tokenExpireBlock
{
    return [self POST:path
requestSerializerType:ERequestSerializerTypeHTTP
responseSerializerType:EResponseSerializerTypeJSON
         headerParams:headerParams
          queryParams:queryParams
           formParams:formParams
            withToken:withToken
                model:modelClass
             progress:progressBlock
              success:successBlock
              failure:failureBlock
             netError:netErrorBlock
          tokenExpire:tokenExpireBlock];
}


- (NSURLSessionTask *)POSTWithJSONRequestSerialized:(NSString *)path
                                       headerParams:(NSDictionary *)headerParams
                                        queryParams:(NSDictionary *)queryParams
                                         formParams:(id)formParams
                                          withToken:(BOOL)withToken
                                              model:(Class)modelClass
                                           progress:(TMRequestProgressBlock)progressBlock
                                            success:(TMRequestSuccessBlock)successBlock
                                            failure:(TMRequestFailureBlock)failureBlock
{
    return [self POSTWithJSONRequestSerialized:path
                                  headerParams:headerParams
                                   queryParams:queryParams
                                    formParams:formParams
                                     withToken:withToken
                                         model:modelClass
                                      progress:progressBlock
                                       success:successBlock
                                       failure:failureBlock
                                      netError:nil
                                   tokenExpire:nil];
}

- (NSURLSessionTask *)POSTWithJSONRequestSerialized:(NSString *)path
                                       headerParams:(NSDictionary *)headerParams
                                        queryParams:(NSDictionary *)queryParams
                                         formParams:(id)formParams
                                          withToken:(BOOL)withToken
                                              model:(Class)modelClass
                                           progress:(TMRequestProgressBlock)progressBlock
                                            success:(TMRequestSuccessBlock)successBlock
                                            failure:(TMRequestFailureBlock)failureBlock
                                           netError:(TMRequestNetErrorBlock)netErrorBlock
                                        tokenExpire:(TMRequestTokenExpireBlock)tokenExpireBlock
{
    return [self POST:path
requestSerializerType:ERequestSerializerTypeJSON
responseSerializerType:EResponseSerializerTypeJSON
         headerParams:headerParams
          queryParams:queryParams
           formParams:formParams
            withToken:withToken
                model:modelClass
             progress:progressBlock
              success:successBlock
              failure:failureBlock
             netError:netErrorBlock
          tokenExpire:tokenExpireBlock];
}

- (NSURLSessionTask *)POST:(NSString *)path
     requestSerializerType:(TMRequestSerializerType)requestSerializerType
    responseSerializerType:(TMResponseSerializerType)responseSerializerType
              headerParams:(NSDictionary *)headerParams
               queryParams:(NSDictionary *)queryParams
                formParams:(id)formParams
                 withToken:(BOOL)withToken
                     model:(Class)modelClass
                  progress:(TMRequestProgressBlock)progressBlock
                   success:(TMRequestSuccessBlock)successBlock
                   failure:(TMRequestFailureBlock)failureBlock
                  netError:(TMRequestNetErrorBlock)netErrorBlock
               tokenExpire:(TMRequestTokenExpireBlock)tokenExpireBlock
{
    return [self requestWithPath:path
                      httpMethod:EHTTPMethodTypePost
           requestSerializerType:requestSerializerType
          responseSerializerType:responseSerializerType
                    headerParams:headerParams
                     queryParams:queryParams
                      formParams:formParams
                       withToken:withToken
       constructingBodyWithBlock:nil
                           model:modelClass
                        progress:progressBlock
                         success:successBlock
                         failure:failureBlock
                        netError:netErrorBlock
                     tokenExpire:tokenExpireBlock];
}

- (NSURLSessionTask *)POST:(NSString *)path
              headerParams:(NSDictionary *)headerParams
               queryParams:(NSDictionary *)queryParams
                formParams:(id)formParams
                 withToken:(BOOL)withToken
 constructingBodyWithBlock:(AFConstructionBlock)constructingBlock
                     model:(Class)modelClass
                  progress:(TMRequestProgressBlock)progressBlock
                   success:(TMRequestSuccessBlock)successBlock
                   failure:(TMRequestFailureBlock)failureBlock
{
    return [self POST:path
         headerParams:headerParams
          queryParams:queryParams
           formParams:formParams
            withToken:withToken
constructingBodyWithBlock:constructingBlock
                model:modelClass
             progress:progressBlock
              success:successBlock
              failure:failureBlock
             netError:nil
          tokenExpire:nil];
}

- (NSURLSessionTask *)POST:(NSString *)path
              headerParams:(NSDictionary *)headerParams
               queryParams:(NSDictionary *)queryParams
                formParams:(id)formParams
                 withToken:(BOOL)withToken
 constructingBodyWithBlock:(AFConstructionBlock)constructingBlock
                     model:(Class)modelClass
                  progress:(TMRequestProgressBlock)progressBlock
                   success:(TMRequestSuccessBlock)successBlock
                   failure:(TMRequestFailureBlock)failureBlock
                  netError:(TMRequestNetErrorBlock)netErrorBlock
               tokenExpire:(TMRequestTokenExpireBlock)tokenExpireBlock
{
    return [self POST:path
requestSerializerType:ERequestSerializerTypeHTTP
responseSerializerType:EResponseSerializerTypeJSON
         headerParams:headerParams
          queryParams:queryParams
           formParams:formParams
            withToken:withToken
constructingBodyWithBlock:constructingBlock
                model:modelClass
             progress:progressBlock
              success:successBlock
              failure:failureBlock
             netError:netErrorBlock
          tokenExpire:tokenExpireBlock];
}

- (NSURLSessionTask *)POST:(NSString *)path
     requestSerializerType:(TMRequestSerializerType)requestSerializerType
    responseSerializerType:(TMResponseSerializerType)responseSerializerType
              headerParams:(NSDictionary *)headerParams
               queryParams:(NSDictionary *)queryParams
                formParams:(id)formParams
                 withToken:(BOOL)withToken
 constructingBodyWithBlock:(AFConstructionBlock)constructingBlock
                     model:(Class)modelClass
                  progress:(TMRequestProgressBlock)progressBlock
                   success:(TMRequestSuccessBlock)successBlock
                   failure:(TMRequestFailureBlock)failureBlock
                  netError:(TMRequestNetErrorBlock)netErrorBlock
               tokenExpire:(TMRequestTokenExpireBlock)tokenExpireBlock
{
    return [self requestWithPath:path
                      httpMethod:EHTTPMethodTypePost
           requestSerializerType:requestSerializerType
          responseSerializerType:responseSerializerType
                    headerParams:headerParams
                     queryParams:queryParams
                      formParams:formParams
                       withToken:withToken
       constructingBodyWithBlock:constructingBlock
                           model:modelClass
                        progress:progressBlock
                         success:successBlock
                         failure:failureBlock
                        netError:netErrorBlock
                     tokenExpire:tokenExpireBlock];
}

#pragma mark - Static Method

+ (TMNetwork *)sharedInstance
{
    static id sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

@end
